--- Decimal numbers, after Douglas Crockfords idea
--- See 'http://dec64.com/'
protected module frege.prelude.PreludeDecimal
        inline(Decimal.coefficient, Decimal.exponent, Decimal.isZero, Decimal.isNaN) 
    where

import Prelude.PreludeBase(Long, Int, negate, Eq, Ord, otherwise, not, &&)

protected pure native >>      :: Long → Int  → Long
protected pure native <<      :: Long → Int  → Long
protected pure native &       :: Long → Long → Long
protected pure native ¦  "|"  :: Long → Long → Long

data Decimal = pure native "long" where
    {-- 
        Interpret the bits of the 'Long' value as a 'Decimal'
        This is a no-operation, as 'Decimal's *are*  'Long's and every 'Long' value is a valid 'Decimal'
        (though it might not be a number, see also 'Decimal.nan').
    -} 
    pure native fromBits "(long)" :: Long    → Decimal
    {-- 
        Interpret the bits of a 'Decimal' as 'Long' value.
        The result will look like this
        
        > mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm eeee eeee
        >         54        48        40        32        24        16        8 7        0
        - The 8  e bits 0..7  are the exponent in 2's complement encoding
        - The 56 m bits 8..63 are the coefficient in 2's complement encodeing
        - an exponent of -128 (0x80) signals not-a-number (NaN)
        - the value of a number that is not NaN is coefficient*(exp^10)
        
        Note that there are usually several encodings for one and the same value.
        1, for example, could be 0x100 or 0xAFF (10E-1)
        Likewise 5 could be represented as 5e0, 50e-1, 0.5e1 and so forth.
        
        This is a no-operation, as 'Decimal's *are* 'Long's. 
        However, the Frege compiler sees them as totally unrelated types (and rightly so). 
    -}
    pure native toBits   "(long)" :: Decimal → Long
    
    --- the smalles possible coefficient is -36028797018963968
    pure native minCoefficient " 0xFF80000000000000L" :: Long
    
    --- the largest possible coefficient is 36028797018963967
    pure native maxCoefficient " 0x007FFFFFFFFFFFFFL" :: Long
    
    --- the smallest exponent is -127
    pure native minExponent " 0xffffff81" :: Int
    
    --- the largest exponent is 127
    pure native maxExponent " 127" :: Int
    
    --- The canonical _not a number_ value.
    --- Note that there are 2^56 NaN values, all comparing equal.
    pure native nan " 128L" :: Decimal
    
    --- The canonical 'Decimal' 0
    pure native zero " 0L" :: Decimal
    
    --- The canonical 'Decimal' 1
    pure native one  " 0x100L" :: Decimal
    
    --- tell if a 'Decimal' is not a number. This works for all 2^56 NaN values.
    isNaN d = (toBits d) & 0xffL == 128
    
    --- tell if a 'Decimal' is 0. This works for all 255 0 values.
    isZero d = not (isNaN d) && d.coefficient == 0
    
    --- extract the coefficient from a 'Decimal'
    coefficient d = toBits d  >>  8

    --- extract the exponent, sign extended in an 'Int'
    exponent d = ((toBits d & 0xffL).int Int.`shiftL` 24) Int.`shiftR` 24         -- sign extend
    
    {-- 
        construct a 'Decimal' from a coefficient and an exponent
        
        This will be 'Decimal.nan' if the coefficient is not in the range 'minCoefficient' .. 'maxCoefficient'
        or the exponent is not in the range 'minExponent' .. 'maxExponent', *even if* the value could be 
        represented. Thus:
        
        > Decimal.pack 36028797018963967 0     == 36028797018963967z
        > Decimal.pack 360287970189639670 (-1) == nan
        
         
    --}
    pack coeff !exp | coeff < minCoefficient = nan
                    | coeff > maxCoefficient = nan
                    | exp   < minExponent    = nan
                    | exp   > maxExponent    = nan
                    | otherwise              = fromBits ((coeff << 8) ¦ (exp Int.`.&.` 0xFF).long)
    
    --- the sign of a 'Decimal' (either -1, 0 or 1)
    sign ∷ Decimal → Int
    sign d | isZero d = 0
           | d.toBits < 0 = -1
           | otherwise = 1